// 文件移动
const Metalsmith = require("metalsmith");
const Handlebars = require("handlebars");
const path = require("path");
const async = require("async");
const ask = require("./ask");
const filter = require("./filter");
const getOptions = require("./options");
const config = require("./config");
// 内容替换
const consolidate = require("consolidate").handlebars.render;

// 将vuejs-templates/webpack/blob/develop/template/package.json文件注册自定义模版表达式：if_eq、unless_eq
Handlebars.registerHelper("if_eq", function (a, b, opts) {
  return a === b ? opts.fn(this) : opts.inverse(this);
});

Handlebars.registerHelper("unless_eq", function (a, b, opts) {
  return a === b ? opts.inverse(this) : opts.fn(this);
});

module.exports = function generate(src, dest, done) {
  // 获取vuejs-templates/webpack/blob/develop/meta.js整个对象
  const opts = getOptions(src);
  // 将模板移动到目标目录
  const metalsmith = Metalsmith(path.join(src, config.template));
  // 初始化vuejs-templates的meta.js设置默认入参isNotTest
  Object.assign(metalsmith.metadata(), {
    isNotTest: true,
  });
  // 定义模版表达式
  opts.helpers &&
    Object.keys(opts.helpers).map((key) => {
      Handlebars.registerHelper(key, opts.helpers[key]);
    });
  // 根据本地meta.js文件获取prompts，用于交互式问答
  metalsmith.use(askQuestions(opts.prompts));
  // 根据本地meta.js文件获取filters，用于将指定文件过滤
  metalsmith.use(filterFiles(opts.filters));
  // 交互式问答之后替换package.json文件内容
  metalsmith.use(renderTemplateFiles(opts.skipInterpolation));
  // 生成项目
  metalsmith.source(".").destination(dest).clean(false).build(done);
};

// 问题询问（是否安装route、eslint等）
const askQuestions = (prompts) => {
  return (files, metalsmith, done) => {
    // metalsmith.metadata()初始化是空对象需要问答后键值对赋值
    ask(prompts, metalsmith.metadata(), done);
  };
};

const filterFiles = (filters) => {
  return (files, metalsmith, done) => {
    filter(filters, files, metalsmith.metadata(), done);
  };
};

// 将git项目vuejs-templates/webpack/template中的{{}}替换我们输入的内容与选项
const renderTemplateFiles = (skipInterpolation) => {
  return (files, metalsmith, done) => {
    const keys = Object.keys(files);
    const metadata = metalsmith.metadata();
    async.each(
      keys,
      (file, next) => {
        const str = files[file].contents.toString();
        // 匹配 {{ }} 不成功
        if (!/{{([^{}]+)}}/g.test(str)) {
          return next();
        }
        // 匹配 {{ }} 成功
        consolidate(str, metadata, (err, res) => {
          files[file].contents = Buffer.from(res);
          next();
        });
      },
      done
    );
  };
};
